﻿using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Urs.Core;
using Urs.Core.Caching;
using Urs.Data.Domain.Stores;
using Urs.Data.Domain.Common;
using Urs.Data.Domain.Users;
using Urs.Data.Domain.Media;
using Urs.Data.Domain.Orders;
using Urs.Data.Domain.Configuration;
using Plugin.Api.Models.Checkout;
using Plugin.Api.Models.UserAddress;
using Plugin.Api.Models.Media;
using Urs.Services.Stores;
using Urs.Services.Users;
using Urs.Services.Localization;
using Urs.Services.Media;
using Urs.Services.Orders;
using Urs.Services.Payments;
using Urs.Services.Plugins;
using Urs.Services.Shipping;
using Urs.Framework.Controllers;

namespace Plugin.Api.Controllers
{
    /// <summary>
    /// 结算接口
    /// </summary>
    [ApiAuthorize]
    [ApiVersion("1.0")]
    [Route("api/v{version:apiVersion}/checkout")]
    [ApiController]
    public class CheckoutController : BaseApiController
    {
        #region Fields
        private readonly IWorkContext _workContext;
        private readonly OrderSettings _orderSettings;
        private readonly BillingAddressSettings _billingAddressSettings;
        private readonly ShoppingCartSettings _shoppingCartSettings;
        private readonly MediaSettings _mediaSettings;
        private readonly ShippingSettings _shippingSettings;
        private readonly IPaymentService _paymentService;
        private readonly IGoodsSpecParser _goodsSpecParser;
        private readonly IShippingService _shippingService;
        private readonly ILocalizationService _localizationService;
        private readonly IOrderProcessingService _orderProcessingService;
        private readonly IOrderTotalCalculationService _orderTotalCalculationService;
        private readonly IShoppingCartService _shoppingCartService;
        private readonly IPriceCalculationService _priceCalculationService;
        private readonly IGoodsSpecFormatter _goodsSpecFormatter;
        private readonly IWebHelper _webHelper;
        private readonly ICacheManager _cacheManager;
        private readonly IPictureService _pictureService;
        private readonly IUserService _userService;
        private readonly IPluginFinder _pluginFinder;
        #endregion

        /// <summary>
        /// 构造器
        /// </summary>
        #region Constructors
        public CheckoutController(IWorkContext workContext,
            IPaymentService paymentService,
            IGoodsSpecParser goodsSpecParser,
            IShippingService shippingService,
            ILocalizationService localizationService,
            IOrderProcessingService orderProcessingService,
            IOrderTotalCalculationService orderTotalCalculationService,
            IShoppingCartService shoppingCartService,
            IPictureService pictureService,
            IPriceCalculationService priceCalculationService,
            IGoodsSpecFormatter goodsSpecFormatter,
            IWebHelper webHelper,
            ICacheManager cacheManager,
            OrderSettings orderSettings,
            BillingAddressSettings billingAddressSettings,
            ShoppingCartSettings shoppingCartSettings,
            MediaSettings mediaSettings,
            ShippingSettings shippingSettings,
            IUserService userService,
            IPluginFinder pluginFinder)
        {
            this._workContext = workContext;
            this._paymentService = paymentService;
            this._goodsSpecParser = goodsSpecParser;
            this._shippingService = shippingService;
            this._localizationService = localizationService;
            this._orderProcessingService = orderProcessingService;
            this._orderTotalCalculationService = orderTotalCalculationService;
            this._shoppingCartService = shoppingCartService;
            this._pictureService = pictureService;
            this._priceCalculationService = priceCalculationService;
            this._goodsSpecFormatter = goodsSpecFormatter;
            this._webHelper = webHelper;
            this._cacheManager = cacheManager;
            this._orderSettings = orderSettings;
            this._billingAddressSettings = billingAddressSettings;
            this._shoppingCartSettings = shoppingCartSettings;
            this._mediaSettings = mediaSettings;
            this._shippingSettings = shippingSettings;
            this._userService = userService;
            this._pluginFinder = pluginFinder;
        }
        #endregion

        #region Utilities
        [NonAction]
        protected void PrepareShippingMethodModel(MoCheckout checkModel, IList<ShoppingCartItem> cart, User user)
        {
            var model = new MoShippingMethodList();

            checkModel.ShippingMethodRequired = _shippingSettings.Enabled;
            if (!_shippingSettings.Enabled)
                return;

            var getShippingOptionResponse = _shippingService.GetShippingOptions(cart, user.ShippingAddress);
            if (getShippingOptionResponse.Success)
            {
                int count = 0;
                foreach (var shippingOption in getShippingOptionResponse.ShippingOptions)
                {
                    var soModel = new MoShippingMethodList.MoShippingMethodItem()
                    {
                        Name = shippingOption.Name,
                        SystemName = shippingOption.ShippingRateMethodSystemName,
                    };
                    //adjust rate
                    var shippingTotal = _orderTotalCalculationService.AdjustShippingRate(shippingOption.Rate, cart);
                    soModel.Fee = PriceFormatter.FormatPrice(shippingTotal);

                    if (count == 0)
                    {
                        checkModel.Shopping = soModel.Fee;
                    }
                    count++;
                    model.ShippingMethods.Add(soModel);
                }
            }
            else
                foreach (var error in getShippingOptionResponse.Errors)
                    model.Warnings.Add(error);

            checkModel.ShippingMethods = model;
        }
        [NonAction]
        protected void PrepareAddressModel(MoCheckout checkoutModel, Address address)
        {
            if (address != null)
            {
                var model = new MoAddress();
                model.Id = address.Id;
                model.Name = address.Name;
                model.ProvinceName = address.ProvinceName;
                model.CityName = address.CityName;
                model.AreaName = address.AreaName;
                model.Address = address.Address1;
                model.Phone = address.PhoneNumber;

                checkoutModel.Address = model;
            }
        }

        [NonAction]
        protected MoPicture PrepareCartItemPictureModel(ShoppingCartItem sci, Goods goods,
            int pictureSize, bool showDefaultPicture, string goodsName)
        {
            if (goods == null)
                throw new ArgumentNullException("goods");

            Picture picture = _pictureService.GetPicturesByGoodsId(goods.Id, 1).FirstOrDefault();

            return new MoPicture()
            {
                NormalUrl = _pictureService.GetPictureUrl(picture, pictureSize, showDefaultPicture)
            };
        }
        [NonAction]
        protected void PrepareShoppingCartModel(MoCheckout model, IList<ShoppingCartItem> cart, User user)
        {
            if (cart == null)
                throw new ArgumentNullException("cart");

            if (model == null)
                throw new ArgumentNullException("model");

            if (cart.Count == 0)
                return;

            #region Cart items

            foreach (var sci in cart)
            {
                var goods = sci.Goods;

                var cartItemModel = new MoCartItem()
                {
                    Id = sci.Id,
                    GoodsId = goods.Id,
                    GoodsSku = goods.Sku,
                    GoodsName = goods.Name,
                    Quantity = sci.Quantity,
                    AttributeInfo = _goodsSpecFormatter.FormatAttributes(sci.AttributesXml, " "),
                };

                //unit prices
                decimal unitPrice = _priceCalculationService.GetUnitPrice(sci);
                cartItemModel.UnitPrice = PriceFormatter.FormatPrice(unitPrice);
                //subtotal, discount 
                //sub total
                decimal subTotal = _priceCalculationService.GetSubTotal(sci);
                cartItemModel.SubTotal = PriceFormatter.FormatPrice(subTotal);

                //picture
                if (_shoppingCartSettings.ShowGoodsImagesOnShoppingCart)
                {
                    cartItemModel.Picture = PrepareCartItemPictureModel(sci, goods,
                        _mediaSettings.CartThumbPictureSize, true, cartItemModel.GoodsName);
                }

                //item warnings
                var itemWarnings = _shoppingCartService.GetShoppingCartItemWarnings(
                    user,
                    goods,
                    sci.AttributesXml,
                    sci.Quantity,
                    false);
                foreach (var warning in itemWarnings)
                    cartItemModel.Warnings.Add(warning);

                model.Items.Add(cartItemModel);
            }

            #endregion

            #region Cart Total

            decimal shippingTotal = _orderTotalCalculationService.GetShoppingCartShippingTotal(cart, user);
            model.Shopping = PriceFormatter.FormatPrice(shippingTotal);

            decimal cartTotal = _orderTotalCalculationService.GetShoppingCartSubTotal(cart);
            model.OrderSubtotal = PriceFormatter.FormatPrice(cartTotal);
            //total
            decimal orderTotal = _orderTotalCalculationService.GetShoppingCartTotal(cart, user);
            model.OrderTotal = PriceFormatter.FormatPrice(orderTotal);
            #endregion
        }
        #endregion

        /// <summary>
        /// 检查结算金额（最新额度限制）
        /// </summary>
        /// <returns></returns>
        [HttpGet("checkminsubtotal")]
        public async Task<ApiResponse<decimal>> CheckMinOrderSubtotal()
        {
            if (!Guid.TryParse(RouteData.Values["guid"].ToString(), out Guid userGuid))
                return ApiResponse<decimal>.Warn("guid格式不正确");

            var user = _userService.GetUserByGuid(userGuid);
            if (user == null || !user.Active || user.Deleted)
                return ApiResponse<decimal>.Warn("用户不存在或被禁用");

            //validation
            var cart = user.ShoppingCartItems.Where(sci => sci.Selected).ToList();
            if (cart.Count == 0)
                return ApiResponse<decimal>.NotFound();

            bool amountOk = _orderProcessingService.ValidateMinOrderSubtotalAmount(cart);
            decimal amount = _orderSettings.MinOrderSubtotalAmount;
            if (!amountOk)
            {
                var warn = string.Format(_localizationService.GetResource("Checkout.MinOrderSubtotalAmount"), PriceFormatter.FormatPrice(amount));
                return ApiResponse<decimal>.Warn(warn);
            }
            return ApiResponse<decimal>.Success(amount);
        }

        /// <summary>
        /// 获取结算信息
        /// </summary>
        /// <returns></returns>
        [HttpGet("get")]
        [ProducesResponseType(typeof(MoCheckout), 200)]
        public async Task<ApiResponse<MoCheckout>> Get(int PTUserId = 0)
        {
            var user = RegisterUser;
            if (user == null || !user.Active || user.Deleted)
                return ApiResponse<MoCheckout>.Warn("用户不存在或被禁用");


            //validation
            var cart = user.ShoppingCartItems.Where(sci =>  sci.Selected).ToList();
            if (cart.Count == 0)
                return ApiResponse<MoCheckout>.Warn("购物车不能为空");

            var data = await Task.Run(() =>
            {
                var model = new MoCheckout();
                model.ShippingRequired = cart.RequiresShipping();
                //加载默认地址
                PrepareAddressModel(model, user.ShippingAddress);
                //加载默认配送方式
                PrepareShippingMethodModel(model, cart, user);
                //加载购物信息
                PrepareShoppingCartModel(model, cart, user);

                return model;
            });
            return ApiResponse<MoCheckout>.Success(data);
        }

        /// <summary>
        /// 获取支付方式
        /// </summary>
        /// <returns></returns>
        [HttpGet("getpaymentmethod")]
        public async Task<ApiResponse<List<MoPaymentMethod>>> GetPaymentMethod()
        {

            var data = await Task.Run(() =>
            {
                var boundPaymentMethods = _paymentService
                  .LoadActivePaymentMethods()
                  .Where(pm => pm.PaymentMethodType == PaymentMethodType.Standard || pm.PaymentMethodType == PaymentMethodType.Redirection)
                  .ToList();
                var model = new List<MoPaymentMethod>();
                foreach (var pm in boundPaymentMethods)
                {
                    var mpm = new MoPaymentMethod()
                    {
                        Name = pm.GetLocalizedFriendlyName(_localizationService),
                        PaymentMethodSystemName = pm.PluginDescriptor.SystemName
                    };
                    model.Add(mpm);
                }
                return model;
            });

            return ApiResponse<List<MoPaymentMethod>>.Success(data);
        }
        /// <summary>
        /// 获取配送方式
        /// </summary>
        /// <returns></returns>
        [HttpGet("getshippingmethod")]
        public async Task<ApiResponse<List<MoShippingMethod>>> GetShippingMethod()
        {
            var user = RegisterUser;

            if (!_shippingSettings.Enabled)
                return ApiResponse<List<MoShippingMethod>>.Success(new List<MoShippingMethod>());

            //validation
            var cart = user.ShoppingCartItems.Where(sci =>sci.Selected).ToList();
            if (cart.Count == 0)
                return ApiResponse<List<MoShippingMethod>>.NotFound();

            var model = new List<MoShippingMethod>();
            var getShippingOptionResponse = _shippingService.GetShippingOptions(cart, user.ShippingAddress);
            if (getShippingOptionResponse.Success)
            {
                foreach (var shippingOption in getShippingOptionResponse.ShippingOptions)
                {
                    var soModel = new MoShippingMethod()
                    {
                        Name = shippingOption.Name,
                        ShippingMethodSystemName = shippingOption.ShippingRateMethodSystemName,
                    };
                    //adjust rate
                    var shippingTotal = _orderTotalCalculationService.AdjustShippingRate(shippingOption.Rate, cart);

                    soModel.Fee = PriceFormatter.FormatPrice(shippingTotal);

                    model.Add(soModel);
                }
                return ApiResponse<List<MoShippingMethod>>.Success(model);
            }
            return ApiResponse<List<MoShippingMethod>>.Warn(getShippingOptionResponse.Errors.FirstOrDefault());
        }

        /// <summary>
        /// 提交订单
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [HttpPost("submit")]
        public async Task<ApiResponse<MoCheckoutResponse>> Submit(MoCheckoutRequest request)
        {
            var user = RegisterUser;
            if (user == null || !user.Active || user.Deleted)
                return ApiResponse<MoCheckoutResponse>.Warn("用户不存在或被禁用");

            var data = await Task.Run(() =>
            {
                var processRequest = new ProcessPaymentRequest
                {
                    UserId = user.Id,
                    Remark = request.Remark,
                    Balance = request.Balance,
                    CouponId = request.CouponId,
                    PaymentMethod = request.PaymentMethod,
                };
                if (request.AddressId > 0)
                    processRequest.AddressId = request.AddressId;
                else if (request.Address != null && request.AddressId == 0)
                {
                    var address = request.Address;
                    processRequest.Shipping = new ProcessAddressRequest
                    {
                        Name = address.Name,
                        ProvinceName = address.ProvinceName,
                        CityName = address.CityName,
                        AreaName = address.AreaName,
                        PhoneNumber = address.Phone,
                        Address = address.Address
                    };
                }
                var result = _orderProcessingService.PlaceOrder(processRequest);
                return result;
            });

            if (data.Success)
            {
                var model = new MoCheckoutResponse();
                var order = data.PlacedOrder;
                model.OrderId = order.Id;
                model.PaymentMethod = order.PaymentMethodSystemName;
                return ApiResponse<MoCheckoutResponse>.Success(model);
            }
            var error = data.Errors.FirstOrDefault();
            return ApiResponse<MoCheckoutResponse>.Warn(error);
        }
        /// <summary>
        /// 快捷提交订单
        /// </summary>
        /// <param name="request">请求对象</param>
        /// <returns></returns>
        [HttpPost("quicksubmit")]
        public async Task<ApiResponse<MoQuickOrderResponse>> QuickOrder(MoQuickOrderRequest request)
        {
            var user = RegisterUser;
            if (user == null || !user.Active || user.Deleted)
                return ApiResponse<MoQuickOrderResponse>.Warn("用户不存在");

            if (request.GoodsValues.Count == 0)
                return ApiResponse<MoQuickOrderResponse>.Warn("商品不能为空");

            if (request.GoodsValues.FirstOrDefault(q => string.IsNullOrEmpty(q.Sku)) != null)
                return ApiResponse<MoQuickOrderResponse>.Warn("Sku不能为空");

            var data = await Task.Run(() =>
            {
                var processRequest = new ProcessPaymentRequest
                {
                    UserId = user.Id,
                    Balance = request.Balance,
                };
                if (request.AddressId > 0)
                    processRequest.AddressId = request.AddressId;
                if (request.Address != null && request.AddressId == 0)
                {
                    var address = request.Address;
                    processRequest.Shipping = new ProcessAddressRequest
                    {
                        Name = address.Name,
                        ProvinceName = address.ProvinceName,
                        CityName = address.CityName,
                        AreaName = address.AreaName,
                        PhoneNumber = address.Phone,
                        Address = address.Address
                    };
                }
                processRequest.OrderTotal = request.OrderTotal;
                processRequest.Remark = request.Remark;
                processRequest.PaymentMethod = request.PaymentMethod;

                foreach (var item in request.GoodsValues)
                {
                    processRequest.GoodsValues.Add(item.Sku, item.Qty);
                }
                var result = _orderProcessingService.QuickPlaceOrder(processRequest);
                return result;
            });

            if (data.Success)
            {
                var model = new MoQuickOrderResponse();
                var order = data.PlacedOrder;
                model.OrderGuid = order.OrderGuid.ToString();
                model.OrderId = order.Id;
                return ApiResponse<MoQuickOrderResponse>.Success(model);
            }
            var error = data.Errors.FirstOrDefault();
            return ApiResponse<MoQuickOrderResponse>.Warn(error);
        }
    }
}
